package gui.model;

import gui.IFacade;
import gui.IRobot;
import roborally.Robot;

/**
 * @author Molderez Tom, 2de bach Ingenieurswetenschappen Computerwetenschappen-Elektrotechniek
 * @author and Reniers Jorn, 2de bach Ingenieurswetenschappen Werktuigkunde-Computerwetenschappen
 * @version 5.0
 *
 */
public class Facade implements IFacade {
	
	/**
	 * Creates a new robot.
	 * 
	 * @param x The x-coordinate of the robot.
	 * @param y The y-coordinate of the robot.
	 * @param orientation The orientation of the robot.
	 * @param energy The energy level of the robot.
	 *
	 * @return if Robot.isPossibleEnergyLevel(energy) && energy <= Robot.DEFAULT_ENERGY_LIMIT && x >= 0 && y >= 0 && x <= Robot.getXLimit() && y <= Robot.getYLimit()
	 * 		| 	then return == this(x, y, orientation, energy)
	 * 		|  else
	 * 		| 	then return == null
	 *
	 */
	@Override
	public IRobot createRobot(long x, long y, int orientation, double energy) {
		if(Robot.isPossibleEnergyLevel(energy) && energy <= Robot.DEFAULT_ENERGY_LIMIT) {
			try {
				return new Robot(x,y,orientation,energy);
			}
			catch(IllegalArgumentException exc){
				System.err.println(exc.getMessage());
				return null;
			}
		}
		else {
			System.err.println("WARNING: invalid energy level");
			return null;
		}
	}

	/**
	 * Returns the x-coordinate of the robot.
	 * 
	 * @param robot The robot of which x-coordinate must be returned.
	 * @return The x-coordinate of the robot
	 * 		| result == Robot.class.cast(robot).getX()
	 */
	@Override
	public long getX(IRobot robot) {
		return Robot.class.cast(robot).getX();
	}

	/**
	 * Returns the y-coordinate of the robot.
	 * 
	 * @param robot The robot of which y-coordinate must be returned.
	 * @return The y-coordinate of the robot
	 * 		| result == Robot.class.cast(robot).getY()
	 */
	@Override
	public long getY(IRobot robot) {
		return Robot.class.cast(robot).getY();
	}

	/**
	 * Returns the orientation of the robot.
	 * 
	 * @param robot The robot of which orientation must be returned.
	 * @return The orientation of the robot
	 * 		| result == Robot.class.cast(robot).getOrientation()
	 */

	@Override
	public int getOrientation(IRobot robot) {
		return Robot.class.cast(robot).getOrientation();
	}

	/**
	 * Moves the robot one step in his current orientation if possible.
	 * 
	 * Moves the robot one step in his current orientation if possible. When he has not enough energy to move, the robot stays on the 
	 * same position. When he moves to an illegal value (of the grid), the robot will stay on his position.
	 * @param robot The robot to be moved.
	 * @effect Moves the robot if the position one in front the robots orientation is valid and the robot is not terminated and the robot has
	 * 			sufficient energy. Else the robot stays on his current position.
	 * 		| if Robot.class.cast(robot).getEnergyLevel() >= Robot.ENERGY_MOVEMENT && !Robot.class.cast(robot).isTerminated()
	 * 		|	then {
	 * 		|		if Robot.class.cast(robot).getOrientation() == Robot.UP && Robot.class.cast(robot).canHaveAsY(getY() - 1)
	 * 		|			then move()
	 * 		| 		if Robot.class.cast(robot).getOrientation() == Robot.RIGHT) && Robot.class.cast(robot).canHaveAsX(getX() + 1)
	 * 		|			then move()
	 * 		|       if Robot.class.cast(robot).getOrientation() == Robot.DOWN && Robot.class.cast(robot).canHaveAsY(getY() + 1)
	 * 		|			then move()
	 * 		|		if Robot.class.cast(robot).getOrientation() == Robot.LEFT) && Robot.class.cast(robot).canHaveAsX(getX() -1)
	 * 		|			then move()
	 */
	@Override
	public void move(IRobot robot) {
		Robot robot2=(Robot) robot;
		try{
			if(robot2.getEnergyLevel() >= Robot.ENERGY_MOVEMENT)
				robot2.move();
		}
		catch(IllegalArgumentException exc){
			// the new position will be not valid (x or y)
			// nothing must be done, the robot just remains at its initial position
			System.err.println(exc.getMessage());
		}
		catch(IllegalStateException exc2){
			// robot is terminated (not possible since Robots can't be destroyed for the moment)
			System.err.println(exc2.getMessage());
		}
		catch(Exception exc3){
			// robot has invalid orientation (impossible) or other unknown exception has been thrown
			System.err.println(exc3.getMessage());
		}
	}

	/**
	 * Turns the robot clockwise.
	 * 
	 * @param robot The robot to be turned.
	 * @effect Turns the robot clockwise.
	 * 		| Robot.class.cast(robot).turn(Robot.CLOCKWISE)
	 */
	@Override
	public void turnClockwise(IRobot robot) {
		Robot.class.cast(robot).turn(Robot.CLOCKWISE);
	}

	/**
	 * Returns the energy level of the robot.
	 * @param robot The robot of which energy must be returned
	 * @return The energy level of the robot.
	 * 		| result == Robot.class.cast(robot).getEnergyLevel()
	 */
	@Override
	public double getEnergy(IRobot robot) {
		return Robot.class.cast(robot).getEnergyLevel();
	}

	/**
	 * Recharges a robot with a given amount of energy.
	 * @param robot The robot to be recharged.
	 * @param energyAmount The energy amount to be added
	 * @effect If the given energy amount is positive, the robot is not been terminated and the current energy level added with the given amount of
	 * 			energy is not greater then the energy limit of the robot, then recharges the robot with the given amount of energy.
	 * 		| if energyAmount >= 0 && !Robot.class.cast(robot).isTerminated() && Robot.class.cast(robot).canHaveAsEnergyLevel(Robot.class.cast(robot).getEnergyLevel() + energyAmount)
	 * 		| 	then Robot.class.cast(robot).recharge(energyAmount)
	 */
	@Override
	public void recharge(IRobot robot, double energyAmount) {
		Robot robot2=(Robot) robot;
		if(energyAmount<0)
			System.err.println("WARNING: the amount to be added must be positive");
		else if(robot2.isTerminated())
			System.err.println("WARNING: this robot is terminated");
		else if(!(robot2.canHaveAsEnergyLevel(robot2.getEnergyLevel() + energyAmount)))
			System.err.println("WARNING: energy overflow");
		else
			robot2.recharge(energyAmount);
	}

	/**
	 * 
	 */
	@Override
	public int isGetEnergyRequiredToReachAndMoveNextTo16Plus() {
		return 1;
	}

	/**
	 * Returns the energy to reach the given position.
	 * 
	 * @param robot The robot who has to reach the given position.
	 * @param x The x-coordinate of the given position to reach.
	 * @param y The y-coordinate of the given position to reach.
	 * 
	 * @return The minimum energy to reach a particular position. When the x-coordinate or the y-coordinate is not valid, -1 is returned.
	 * 		| if Robot.class.cast(robot).canHaveAsX(x) && Robot.class.cast(robot).canHaveAsY(y)
	 * 		|  then result == Robot.class.cast(robot).getEnergyRequiredToReach(x,y)
	 * 		| else 
	 * 		| 	result == -1D
	 */
	@Override
	public double getEnergyRequiredToReach(IRobot robot, long x, long y) {
		Robot robot2 = (Robot) robot;
		if(robot2.canHaveAsX(x) && robot2.canHaveAsY(y)) 
			return robot2.getEneryRequiredToReach(x, y);
		else
			return -1D;
	}

	/**
	 * Moves the 2 robots as close as possible to each other. If they can not reach each other, a best effort is been done.
	 * 
	 * @param robot The first robot to move together.
	 * @param robot2 The second robot to move together.
	 * @effect When both robots are not terminated, they are moved together as close as possible.
	 * 		| if !Robot.class.cast(robot).isTerminated() && !Robot.class.cast(robot).isTerminated() 
	 * 		| 	then Robot.class.cast(robot).moveNextTo(Robot.class.cast(robot2));
	 */
	@Override
	public void moveNextTo(IRobot robot, IRobot robot2) {
		try {
			Robot.class.cast(robot).moveNextTo(Robot.class.cast(robot2));
		}
		catch (IllegalStateException exc) {
			System.err.print(exc.getMessage());
		}
	}

}
